%%% реализация серверной части
-module(master).
-export([start_master/4]).

%%% запуск мастера
% Nodes     список нод
% Threads   количество потоков на каждой ноде
% Graph     входной граф
start_master(Nodes, Threads, Graph, OutResult) ->
         init(init_slave(Nodes, Graph),
	      Threads,
	      OutResult).

%%% инициализация супервайзера
init(Nodes, Threads, OutResult)  ->
    WorkLoad = run_first_task(Nodes,Threads),
    loop_message(Threads,
		 1,
		 [],
		 Nodes,
		 WorkLoad,
		 OutResult).

%%% инициализация рабочих
% Nodes ноды
% Graph граф
init_slave(Nodes, Graph)->
    lists:map(fun(N)->
		      slave:start(N,Graph)
			  end, Nodes).
    


%%% цикл обработки сообщений
% MaxCountProcess максимальное число процессов на ноде
% CurrentCountProcess текущее количество запущенных процессов на всех нодах
% Tasks очередь задач
% Nodes список нод
% WorkloadNodes нагрузка на нодах
% IO объект вывода
loop_message(MaxCountProcess,
	     CurrentCountProcess,
	     Tasks,
	     Nodes, 
	     WorkloadNodes,
	     IO) ->
    receive
	% обработка завершения процесса на ноде Node
	% Запуск новой задачи
	{finish, Node} ->
	    case Tasks of
		% очередь работ не путса
		[Task| Tail] ->
		    run_task_on_node(Node, Task),
		    balancing_processes(MaxCountProcess,
					CurrentCountProcess,
					Tail,
					Nodes, 
					WorkloadNodes,
					IO);
		[]  ->
		    if
			% завершение оставшихся процессов
			CurrentCountProcess>1 ->
			    loop_message(MaxCountProcess,
					 CurrentCountProcess-1,
					 [],
					 Nodes,
					 WorkloadNodes,
					 IO);
			% завершение работы
			true ->
			    lists:foreach(fun(X)-> X!{die} end, Nodes),
			    finish
		     end		    
	   end;
	% добавление задачи Task в список задач Tasks
	{addTask, Task} ->
	    loop_message(MaxCountProcess,
			 CurrentCountProcess,
			 [Task]++Tasks,
			 Nodes,
			 WorkloadNodes,
			 IO);
	{result_process, Result} ->
	    io:fwrite(IO, "~w ~n",[Result]),
	    loop_message(MaxCountProcess,
			 CurrentCountProcess,
			 Tasks,
			 Nodes,
			 WorkloadNodes,
			 IO)
    end.
balancing_processes(MaxCountProcess,
		    CurrentCountProcess,
		    [],
		    Nodes,
		    WorkloadNodes,
		    IO)->
    loop_message(MaxCountProcess,
		 CurrentCountProcess,
		 [],
		 Nodes,
		 WorkloadNodes,
		 IO);
    
balancing_processes(MaxCountProcess,
		    CurrentCountProcess,
		    [Task | Tasks ],
		    Nodes,
		    WorkloadNodes,
		    IO)->
    CRang = priority_lists:current_rang(WorkloadNodes),
    if
	MaxCountProcess > CRang ->
	    run_task_on_node( priority_lists:top(WorkloadNodes),
			      Task),
	    NW = priority_lists:push(priority_lists:current_rang(WorkloadNodes)+1,
				    priority_lists:top(WorkloadNodes),
				    priority_lists:pop(WorkloadNodes)),  
	    balancing_processes(MaxCountProcess,
				CurrentCountProcess+1,
				Tasks,
				Nodes,
				NW,
				IO);
	true -> 
	    loop_message(MaxCountProcess,
			 CurrentCountProcess,
			 [Task] ++ Tasks,
			 Nodes,
			 WorkloadNodes,
			 IO)
  end.

run_task_on_node(Node, Task) ->
    Node!{run, Task}.

run_first_task([Node | Nodes], Threads) ->
    Self = self(),
    Task = fun(X,N) ->
		   parallel_alg:parallel_cliques(X, N, Self)
	   end,
    run_task_on_node(Node, Task),
    default_process_load(Nodes,
			 priority_lists:push(1, 
					     Node,
					     priority_lists:new(Threads))).

default_process_load([], WorkLoad) ->
    WorkLoad;
default_process_load([Node| Nodes], WorkLoad) ->
    default_process_load(Nodes,
			 priority_lists:push(0,
					     Node,
					     WorkLoad)).
